package aggregate

import (
	"errors"
	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"mine-info-server/domain/plan-manage-context/command"
	entity2 "mine-info-server/domain/plan-manage-context/model/plan-aggregate/entity"
	valueobject2 "mine-info-server/domain/plan-manage-context/model/plan-aggregate/valueobject"
	"mine-info-server/utils"
	"mine-info-server/utils/consts"
	"strconv"
	"sync"
	"time"
)

/*
   @author:zhongyang
   @date:2023/4/2
   @description:计划聚合
*/

var (
	TaskItemNotFound = errors.New("未找到对应待办项")
	PlanDateError    = errors.New("开始时间不能大于结束时间！")
)

type PlanAggregate struct {
	// 互斥锁，保证并发安全
	mu sync.Mutex
	// 计划id
	PlanId string
	// 计划名
	Name string
	// 开始时间
	StartTime string
	// 结束时间
	EndTime string
	// 父计划id
	ParentId string
	// 用户id
	UserId string
	// 创建时间
	CreateTime string
	// 完成时间
	FinishTime string
	// 扩展值对象(存放一些不常用的值)
	ExtendDetail *valueobject2.PlanExtendDetail
	// 计划详情实体
	PlanDetail *entity2.PlanDetail
	// 计划报告（值对象）
	PlanReport *valueobject2.PlanReport
	// 待办实体，一个计划有多个待办，待办里有多个待办项
	Tasks []*entity2.Task
	// 问题集
	Questions []*entity2.Question
	// 子计划
	Children []*PlanAggregate
}

// Create 计划实体的创建计划业务逻辑
func (p *PlanAggregate) Create(ctx *gin.Context) {
	// 创建计划
	// 找到计划所属人
	p.UserId = utils.GetUserID(ctx)
	// 如果没有给父计划id，说明本身就是父计划，传默认值
	if p.ParentId == "" {
		p.ParentId = consts.DefaultPlanID
	}
	// 创建计划详情
	detail := entity2.NewPlanDetail()
	p.AddPlanDetail(detail)
	// 发送【计划已创建】的领域事件
}

func (p *PlanAggregate) AddPlanDetail(detail *entity2.PlanDetail) {
	p.PlanDetail = detail
}

// Delete 将删除计划命令转换为聚合
func (p *PlanAggregate) Delete(ctx *gin.Context, deleteCommand *command.PlanDeleteCommand) {
	p.PlanId = deleteCommand.PlanId
}

func (p *PlanAggregate) Edit(name, startDate, endDate, parentId string) error {
	if startDate > endDate {
		return PlanDateError
	}
	// 更新改变的值即可，相同值不需要更新
	if name != p.Name {
		p.Name = name
	}
	if startDate != p.StartTime {
		p.StartTime = startDate
	}
	if endDate != p.EndTime {
		p.EndTime = endDate
	}
	if parentId != p.ParentId {
		p.ParentId = parentId
	}
	// 发布【计划已修改】的领域事件
	return nil
}

func (p *PlanAggregate) GenerateReport() error {
	if p.FinishTime != consts.DefaultDate {
		return errors.New("请勿重复生成计划报告！")
	}
	// 判断计划是否超时
	p.PlanReport.IsOverTime = 2
	p.CalPlanStatus()
	if p.ExtendDetail.PlanStatus == consts.OutOfTime {
		p.PlanReport.IsOverTime = 1
	}
	// 计算预计天数
	p.PlanReport.ExpectDay = calTotalPlanDay(p.StartTime, p.EndTime)
	// 计算实际天数
	p.PlanReport.RealDay = calTotalPlanDay(p.StartTime, time.Now().Format("2006-01-02"))
	// 计算任务数
	for _, task := range p.Tasks {
		p.PlanReport.TaskTotal += int64(len(task.TaskItems))
	}
	// 生成计划完成时间
	p.FinishTime = time.Now().Format("2006-01-02")
	return nil
}

func (p *PlanAggregate) EditTarget(planId string, target string) error {
	// 判断是否是该计划的目标属性
	if p.PlanId != planId {
		return errors.New("修改异常，获取非法计划聚合")
	}
	// 再判断目标值是否相同，如果相同就不需要进行修改
	if p.PlanDetail.Target == target {
		return errors.New("目标未修改")
	}
	// 修改目标
	p.PlanDetail.Target = target
	return nil
}

func (p *PlanAggregate) SubmitReport(realDay int64, expectDay int64, isOverTime int64, taskTotal int64) {
	// 提交报告
	p.PlanReport.ReportId = uuid.New().String()
	p.PlanReport.IsOverTime = isOverTime
	p.PlanReport.RealDay = realDay
	p.PlanReport.ExpectDay = expectDay
	p.PlanReport.TaskTotal = taskTotal

	// 完成计划
	p.FinishTime = time.Now().Format("2006-01-02")
}

func (p *PlanAggregate) CalTaskTotal(total int64) {
	p.ExtendDetail.TaskTotal = total
}

func (p *PlanAggregate) AddChildrenPlan(childrenPlan []*PlanAggregate) {
	p.Children = append(p.Children, childrenPlan...)
}

func calTotalPlanDay(start, end string) int64 {
	startDate, _ := time.ParseInLocation("2006-01-02", start, time.Local)
	endDate, _ := time.ParseInLocation("2006-01-02", end, time.Local)
	days := endDate.Sub(startDate).Hours() / 24
	return int64(days)
}

// CalPlanStatus 计算计划状态
func (p *PlanAggregate) CalPlanStatus() {
	if p.FinishTime != consts.DefaultDate {
		p.ExtendDetail.PlanStatus = consts.Finished
		return
	}
	parseEndTime, _ := time.ParseInLocation("2006-01-02", p.EndTime, time.Local)
	if time.Now().After(parseEndTime) {
		p.ExtendDetail.PlanStatus = consts.OutOfTime
		return
	}
	parseStartTime, _ := time.ParseInLocation("2006-01-02", p.StartTime, time.Local)
	if time.Now().Before(parseStartTime) {
		p.ExtendDetail.PlanStatus = consts.NotStart
		return
	}
	if time.Now().After(parseStartTime) && time.Now().Before(parseEndTime) {
		p.ExtendDetail.PlanStatus = consts.Doing
		return
	}
	p.ExtendDetail.PlanStatus = 5
}

func (p *PlanAggregate) CalSurplusTime() {
	// 如果已经完成就不需要计算剩余时间了
	if p.FinishTime != consts.DefaultDate {
		p.ExtendDetail.SurplusTime = "0分钟"
		return
	}
	parseStartTime, _ := time.ParseInLocation("2006-01-02", p.StartTime, time.Local)
	parseEndTime, _ := time.ParseInLocation("2006-01-02", p.EndTime, time.Local)
	now := time.Now()
	// 如果现在时间比开始时间早，就按【结束时间 - 开始时间】算，否则就按【结束时间 - 现在时间】算。
	if parseStartTime.After(time.Now()) {
		now = parseStartTime
	}
	duration := parseEndTime.Sub(now)
	hours := duration.Hours()
	// 大于24小时按天计算
	if hours >= 24 {
		days := strconv.FormatFloat(hours/24, 'f', 0, 64)
		p.ExtendDetail.SurplusTime = days + "天"
		return
	}
	// 小于24小时 按小时计算
	if hours < 24 && hours > 0 {
		formatHour := strconv.FormatFloat(hours, 'f', 0, 64)
		p.ExtendDetail.SurplusTime = formatHour + "小时"
		return
	}
	minutesFloat := duration.Minutes()
	if minutesFloat < 0 {
		p.ExtendDetail.SurplusTime = "0分钟"
		return
	}
	minutes := strconv.FormatFloat(minutesFloat, 'f', 0, 64)

	p.ExtendDetail.SurplusTime = minutes + "分钟"
}

func (p *PlanAggregate) CreateTask(executeDate string, content string, level int) {
	// 如果没有建立待办就先建一个
	if len(p.Tasks) == 0 {
		task := entity2.NewTask()
		task.Create(executeDate)
		p.AppendTask(task)
	}
	// 创建待办项
	taskItem := entity2.NewTaskItem(content, level)
	// 这里创建的时候聚合根内的待办实体只会有一个
	// 前面已经做过了长度校验，所以这里不需要安全检查
	p.Tasks[0].TaskItems = append(p.Tasks[0].TaskItems, taskItem)
}

func (p *PlanAggregate) AppendTask(task *entity2.Task) {
	p.Tasks = append(p.Tasks, task)
}

// EditTaskItem 编辑待办项
func (p *PlanAggregate) EditTaskItem(level int, content string, executeDate string) error {
	// 先做安全检查
	if len(p.Tasks) == 0 || len(p.Tasks[0].TaskItems) == 0 {
		return TaskItemNotFound
	}
	if p.Tasks[0].TaskItems[0].Content != content {
		p.Tasks[0].TaskItems[0].Content = content
	}
	if p.Tasks[0].TaskItems[0].Level != level {
		p.Tasks[0].TaskItems[0].Level = level
	}
	return nil
}

func (p *PlanAggregate) FinishTaskItem() error {
	// 先做检查
	if len(p.Tasks) == 0 || len(p.Tasks[0].TaskItems) == 0 {
		return TaskItemNotFound
	}
	p.Tasks[0].TaskItems[0].FinishDate = time.Now().Format("2006-01-02")
	return nil
}

// 保存问题
func (p *PlanAggregate) DeleteQuestions(questions []*command.QuestionItemCommand) []*entity2.Question {
	// key为问题id
	questionReqMap := make(map[string]interface{})
	for _, question := range questions {
		questionReqMap[question.QuestionId] = nil
	}
	// 找出被删除的元素
	var deleteQuestions []*entity2.Question
	for index, question := range p.Questions {
		if _, ok := questionReqMap[question.QuestionId]; !ok {
			deleteQuestion := &entity2.Question{
				QuestionId: question.QuestionId,
				Question:   question.Question,
				Answer:     question.Answer,
				PlanId:     question.PlanId,
				UserId:     question.UserId,
				TaskDate:   question.TaskDate,
			}
			deleteQuestions = append(deleteQuestions, deleteQuestion)
			// 同时把聚合根内的问题集也删除
			p.Questions = append(p.Questions[:index], p.Questions[index+1:]...)
		}
	}
	return deleteQuestions
}

func (p *PlanAggregate) SaveQuestions(questions []*command.QuestionItemCommand, userId string) {
	for _, question := range questions {
		// 这里有些是新问题，所以没有用户id和主键id，这里需要给他生成
		if question.QuestionId == "" {
			question.QuestionId = uuid.New().String()
		}
		if question.UserId == "" {
			question.UserId = userId
		}
		entity := &entity2.Question{
			QuestionId: question.QuestionId,
			Question:   question.Question,
			Answer:     question.Answer,
			PlanId:     question.PlanId,
			UserId:     question.UserId,
			TaskDate:   question.TaskDate,
		}
		p.Questions = append(p.Questions, entity)
	}
}

func (p *PlanAggregate) ReplaceTask(task *entity2.Task) {
	if len(p.Tasks) == 0 {
		return
	}
	p.Tasks[0].TaskId = task.TaskId
	p.Tasks[0].ExecuteDate = task.ExecuteDate
	p.Tasks[0].Think = task.Think
	p.Tasks[0].IsNew = task.IsNew
}
